#include "../display/dispcolor.h"
#include "../display/rgbcolor.h"
#include "../palette/palette.h"
#include "../idw/IDW.h"
#include "../settings/settings.h"
#include "../menu/menu.h"
#include "../func.h"
#include "../version.h"
#include "../buttons/buttons.h"
#include "../mlx90640/mlx.h"
#include "../ui/ui.h"
#include "../sleep/sleep.h"

//#define iSteps					9		// Number of intermediate points for horizontal / vertical interpolation 
#define iSteps					9		// Number of intermediate points for horizontal / vertical interpolation 

uint8_t Need2RedrawTitle = 0;
uint8_t Need2RedrawPalette = 0;

static int16_t *TermoImage16;
static int16_t *TermoHqImage16;
float *gaussBuff;
static int16_t *irImageBuff;
static uint16_t PaletteSteps = 0;
static tRGBcolor *pPaletteImage;
static tRGBcolor pPaletteScale[termHeight * 9];//9

uint16_t imageHeight;
uint16_t imageWidth;

static float minTemp = 0;
static float maxTemp = 0;
float minTempNew = SCALE_DEFAULT_MIN;
float maxTempNew = SCALE_DEFAULT_MAX;


//==============================================================================
// The procedure for drawing a thermogram at its original resolution
//==============================================================================
void DrawImage(int16_t *pImage, tRGBcolor *pPalette, uint16_t PaletteSize, uint16_t X, uint16_t Y, uint8_t pixelWidth, uint8_t pixelHeight, float minTemp)
{
	int cnt = 0;
	for (int row = 0; row < 24; row++)
	{
		for (int col = 0; col < 32; col++, cnt++)
		{
			int16_t colorIdx = pImage[cnt] - (minTemp * 10);

			if (colorIdx < 0)
				colorIdx = 0;
			if (colorIdx >= PaletteSize)
				colorIdx = PaletteSize - 1;

	    	uint16_t color = RGB565(pPalette[colorIdx].r, pPalette[colorIdx].g, pPalette[colorIdx].b);
			dispcolor_FillRect((31 - col) * pixelWidth + X, row * pixelHeight + Y, pixelWidth, pixelHeight, color);
		}
	}
}
//==============================================================================


//==============================================================================
// Interpolated thermogram drawing procedure
//==============================================================================
void DrawHQImage(int16_t *pImage, tRGBcolor *pPalette, uint16_t PaletteSize, uint16_t X, uint16_t Y, uint16_t width, uint16_t height, float minTemp)
{
	int cnt = 0;
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++, cnt++)
		{
			int16_t colorIdx = pImage[cnt] - (minTemp * 10);

			if (colorIdx < 0)
				colorIdx = 0;
			if (colorIdx >= PaletteSize)
				colorIdx = PaletteSize - 1;

	    	uint16_t color = RGB565(pPalette[colorIdx].r, pPalette[colorIdx].g, pPalette[colorIdx].b);
	    	dispcolor_DrawPixel((width - col - 1) + X, row + Y, color);
		}
	}
}
//==============================================================================

//==============================================================================
// Procedure for drawing a color bar
//==============================================================================
void DrawScale(uint16_t X, uint16_t Y, uint16_t Width, uint16_t Height, float minTemp, float maxTemp)
{
	getPalette(ColorScale, Height, pPaletteScale);

    for (int i = 0; i < Height; i++)
	{
		uint16_t color = RGB565(pPaletteScale[i].r, pPaletteScale[i].g, pPaletteScale[i].b);
		dispcolor_FillRect(X, Y + Height - i - 1, Width, 1, color);
	}

    // Display of the maximum value in the scale (horizontally - centered)
    int16_t TextWidth = dispcolor_getFormatStrWidth(FONTID_6X8M, "%.0f�C", maxTemp);
    dispcolor_printf(X + (Width - TextWidth) / 2, Y + 2, FONTID_6X8M, ColorScale == Rainbow || ColorScale == Rainbow2 || ColorScale == BlueRed ? WHITE : BLACK, "%.0f�C", maxTemp);
    // Display of the minimum value in the scale (horizontally - centered)
    TextWidth = dispcolor_getFormatStrWidth(FONTID_6X8M, "%.0f�C", minTemp);
    dispcolor_printf(X + (Width - TextWidth) / 2, Y + Height - 10, FONTID_6X8M, WHITE, "%.0f�C", minTemp);
}
//==============================================================================


//==============================================================================
// The procedure for displaying the "sight" and the temperature value in the center of the thermogram in a given color
//==============================================================================
static void DrawCenterTempColor(uint16_t cX, uint16_t cY, float Temp, uint16_t color)
{
	uint8_t offMin = 5;		// Distance from the center to the beginning of the marks
	uint8_t offMax = 10;	// Distance from the center to the end of the lines
	uint8_t offTwin = 1;	// Distance between 2 parallel risks

	// Top of the crosshair
	dispcolor_DrawLine(cX - offTwin, cY - offMin, cX - offTwin, cY - offMax, color);
	dispcolor_DrawLine(cX + offTwin, cY - offMin, cX + offTwin, cY - offMax, color);
	// Bottom of the crosshair
	dispcolor_DrawLine(cX - offTwin, cY + offMin, cX - offTwin, cY + offMax, color);
	dispcolor_DrawLine(cX + offTwin, cY + offMin, cX + offTwin, cY + offMax, color);
	// Left side of the crosshair
	dispcolor_DrawLine(cX - offMin, cY - offTwin, cX - offMax, cY - offTwin, color);
	dispcolor_DrawLine(cX - offMin, cY + offTwin, cX - offMax, cY + offTwin, color);
	// Right side of the crosshair
	dispcolor_DrawLine(cX + offMin, cY - offTwin, cX + offMax, cY - offTwin, color);
	dispcolor_DrawLine(cX + offMin, cY + offTwin, cX + offMax, cY + offTwin, color);

	if ((Temp > -100) && (Temp < 500))
		dispcolor_printf(cX + 8, cY + 8, FONTID_6X8M, color, "%.1f�C", Temp);
}
//==============================================================================


//==============================================================================
// The procedure for displaying the "sight" and the temperature value in the center of the thermogram in white with a black shadow
//==============================================================================
void DrawCenterTemp(uint16_t X, uint16_t Y, uint16_t Width, uint16_t Height, float Temp)
{
	uint16_t cX = (Width >> 1) + X;
	uint16_t cY = (Height >> 1) + Y;

	// Drawing the shadow to black
	DrawCenterTempColor(cX + 1, cY + 1, Temp, BLACK);
	// Rendering white
	DrawCenterTempColor(cX, cY, Temp, WHITE);
}
//==============================================================================


static void ThermoToImagePosition(int16_t *pX, int16_t *pY)
{
	switch (ScaleMode)
	{
	case ByNearest:
		*pX = *pX * 9;	// 31 * 9 = 279
		*pY = *pY * 9 ;	// 23 * 9 = 207
		break;
	case LineInterpol:
		//*pX = *pX * 69 / 8;	// 31 * 9 = 279
		//*pY = *pY * 69 / 8;	// 23 * 9 = 207
		*pX = *pX * 69 / 9.1;	// 31 * 9 = 279
		*pY = *pY * 69 / 9.1;	// 23 * 9 = 207
		break;
	case HQ3X_2X:
		*pX = *pX * 8;	// 31 * 9 = 279
		*pY = *pY * 8;	// 23 * 9 = 207
		break;
	}
}

static void DrawMarkers(uint16_t imageX, uint16_t imageY, sMlxData *pMlxData)
{
	uint8_t lineHalf = 4;
	int16_t x = termWidth - pMlxData->maxT_X - 1;
	int16_t y = pMlxData->maxT_Y;

	ThermoToImagePosition(&x, &y);
	x += imageX + 4;
	y += imageY + 4;

	uint16_t mainColor = WHITE;
	dispcolor_DrawLine(x + 1, y - lineHalf + 1, x + 1, y + lineHalf + 1, BLACK);
	dispcolor_DrawLine(x - lineHalf + 1, y + 1, x + lineHalf + 1, y + 1, BLACK);

	dispcolor_DrawLine(x - lineHalf + 1, y - lineHalf + 1, x + lineHalf + 1, y + lineHalf + 1, BLACK);
	dispcolor_DrawLine(x - lineHalf + 1, y + lineHalf + 1, x + lineHalf + 1, y - lineHalf + 1, BLACK);

	dispcolor_DrawLine(x, y - lineHalf, x, y + lineHalf, mainColor);
	dispcolor_DrawLine(x - lineHalf, y, x + lineHalf, y, mainColor);

	dispcolor_DrawLine(x - lineHalf, y - lineHalf, x + lineHalf, y + lineHalf, mainColor);
	dispcolor_DrawLine(x - lineHalf, y + lineHalf, x + lineHalf, y - lineHalf, mainColor);

	x = termWidth - pMlxData->minT_X - 1;
	y = pMlxData->minT_Y;

	ThermoToImagePosition(&x, &y);
	x += imageX + 4;
	y += imageY + 4;

//	mainColor = RGB565(200, 200, 255);
	dispcolor_DrawLine(x - lineHalf + 1, y - lineHalf + 1, x + lineHalf + 1, y + lineHalf + 1, BLACK);
	dispcolor_DrawLine(x - lineHalf + 1, y + lineHalf + 1, x + lineHalf + 1, y - lineHalf + 1, BLACK);
	dispcolor_DrawLine(x - lineHalf, y - lineHalf, x + lineHalf, y + lineHalf, mainColor);
	dispcolor_DrawLine(x - lineHalf, y + lineHalf, x + lineHalf, y - lineHalf, mainColor);
}

static void DrawMarkersHQ(uint16_t imageX, uint16_t imageY, sMlxData *pMlxData)
{
	uint8_t lineHalf = 3;
	int16_t x = termWidth - pMlxData->maxT_X - 1;
	int16_t y = pMlxData->maxT_Y;

	ThermoToImagePosition(&x, &y);
	x += imageX + 3;
	y += imageY + 3;

	uint16_t mainColor = WHITE;
	dispcolor_DrawLine(x + 1, y - lineHalf + 1, x + 1, y + lineHalf + 1, BLACK);
	dispcolor_DrawLine(x - lineHalf + 1, y + 1, x + lineHalf + 1, y + 1, BLACK);

	dispcolor_DrawLine(x - lineHalf + 1, y - lineHalf + 1, x + lineHalf + 1, y + lineHalf + 1, BLACK);
	dispcolor_DrawLine(x - lineHalf + 1, y + lineHalf + 1, x + lineHalf + 1, y - lineHalf + 1, BLACK);

	dispcolor_DrawLine(x, y - lineHalf, x, y + lineHalf, mainColor);
	dispcolor_DrawLine(x - lineHalf, y, x + lineHalf, y, mainColor);

	dispcolor_DrawLine(x - lineHalf, y - lineHalf, x + lineHalf, y + lineHalf, mainColor);
	dispcolor_DrawLine(x - lineHalf, y + lineHalf, x + lineHalf, y - lineHalf, mainColor);

	x = termWidth - pMlxData->minT_X - 1;
	y = pMlxData->minT_Y;

	ThermoToImagePosition(&x, &y);
	x += imageX + 3;
	y += imageY + 3;

//	mainColor = RGB565(200, 200, 255);
	dispcolor_DrawLine(x - lineHalf + 1, y - lineHalf + 1, x + lineHalf + 1, y + lineHalf + 1, BLACK);
	dispcolor_DrawLine(x - lineHalf + 1, y + lineHalf + 1, x + lineHalf + 1, y - lineHalf + 1, BLACK);
	dispcolor_DrawLine(x - lineHalf, y - lineHalf, x + lineHalf, y + lineHalf, mainColor);
	dispcolor_DrawLine(x - lineHalf, y + lineHalf, x + lineHalf, y - lineHalf, mainColor);
}
//==============================================================================
// Battery icon display procedure
//==============================================================================
void DrawBattery(uint16_t X, uint16_t Y, float capacity)
{
	// ���������� ���� �������
	uint16_t Color = GREEN;
	if (capacity < 80)
		Color = RGB565(249, 166, 2);
	if (capacity < 50)
		Color = RED;

	// Draw the outline of the battery
	dispcolor_DrawRectangle(X, Y, X + 17, Y + 9, WHITE);
	dispcolor_DrawRectangleFilled(X + 17, Y + 2, X + 19, Y + 6, WHITE);
	// Draw the divisions
	dispcolor_DrawRectangleFilled(X + 12, Y + 2, X + 15, Y + 7, capacity < 80 ? BLACK : Color);
	dispcolor_DrawRectangleFilled(X + 7, Y + 2, X + 10, Y + 7, capacity < 50 ? BLACK : Color);
	dispcolor_DrawRectangleFilled(X + 2, Y + 2, X + 5, Y + 7, capacity < 25 ? BLACK : Color);
}
//==============================================================================


void ProcessButtonEvents(void)
{
	if (xButtonsEventQueue)
	{
		uint8_t event = No_Event;
        if (xQueueReceive(xButtonsEventQueue, &event, (TickType_t) 1))
        {
        	switch (event)
        	{
        	case ShortPress_Up:
        		FuncUp_Run();
        		break;
        	case Hold_Up:
				Deep_Sleep_Run();
				break;
            case ShortPress_Center:
                FuncCenter_Run();
                break;
        	case Hold_Center:
        		menu_run();
        		settings_write_all();
        		dispcolor_ClearScreen();
        		Need2RedrawTitle = 1;
        		Need2RedrawPalette = 1;
        		break;
        	case ShortPress_Down:
        		FuncDown_Run();
        		break;
        	case Hold_Down:
				break;
        	}
        }
	}
}

void ReDrawTitle(void)
{
    dispcolor_printf(25, 4, FONTID_6X8M, WHITE, "Ver V%d.%d  %.1f FPS\r\n", SW_VERSION_MAJOR, SW_VERSION_MINOR, FPS_rates[FPS_Idx]);
}

void RedrawPalette(void)
{
	// Drawing the scale on the right side of the screen
	//DrawScale(imageWidth + 2, (dispHeight - imageHeight) >> 1, dispWidth - imageWidth - 2, imageHeight, minTemp, maxTemp);
	// Generating a new color scale
	PaletteSteps = (uint16_t)((maxTemp - minTemp) * 10 + 1);
	getPalette(ColorScale, PaletteSteps, pPaletteImage);
}

int8_t AllocThermoImageBuffers(void)
{
	// Buffers for scaling algorithms, source and final image
	// 问题

	//MALLOC_CAP_SPIRAM
	printf("0 step. AllocThermoImageBuffers -> into function.\r\n");
    TermoImage16 = heap_caps_malloc((termWidth * termHeight) << 1, MALLOC_CAP_8BIT/* | MALLOC_CAP_INTERNAL*/);//ok
	if (!TermoImage16)
		printf("1 step. AllocThermoImageBuffers -> heap_caps_malloc run over & failed.\r\n");
	else
		printf("1 step. AllocThermoImageBuffers -> heap_caps_malloc run over & ok.\r\n");

	TermoHqImage16 = heap_caps_malloc((termWidth * 8 * termHeight * 8) << 1, MALLOC_CAP_8BIT/*| MALLOC_CAP_INTERNAL*/);//failed TermoHqImage16 = heap_caps_malloc((termWidth * 9 * termHeight * 9) << 1, MALLOC_CAP_8BIT /*| MALLOC_CAP_INTERNAL*/);
	if (!TermoHqImage16)
		printf("2 step. AllocThermoImageBuffers -> heap_caps_malloc run over & failed.\r\n");
	else
		printf("2 step. AllocThermoImageBuffers -> heap_caps_malloc run over & ok.\r\n");

	//gaussBuff = heap_caps_malloc(((termWidth * 4) * (termHeight * 4)) << 1, MALLOC_CAP_8BIT /*| MALLOC_CAP_SPIRAM*/);//ok
/*	if (!gaussBuff)
		printf("3 step. AllocThermoImageBuffers -> heap_caps_malloc run over & failed.\r\n");
	else
		printf("3 step. AllocThermoImageBuffers -> heap_caps_malloc run over & ok.\r\n");
*/    
//	irImageBuff = heap_caps_malloc(((termWidth * 8) * (termHeight * 8) * 2) << 1, MALLOC_CAP_8BIT /*| MALLOC_CAP_SPIRAM*/);//failed irImageBuff = heap_caps_malloc(((termWidth * 8) * (termHeight * 8) * 2) << 1, MALLOC_CAP_8BIT /*| MALLOC_CAP_SPIRAM*/);
/*	if (!irImageBuff)
		printf("4 step. AllocThermoImageBuffers -> heap_caps_malloc run over & failed.\r\n");
	else
		printf("4 step. AllocThermoImageBuffers -> heap_caps_malloc run over & ok.\r\n");
*/
	if (!TermoImage16)
    	return -1;
//    if (!TermoImage16 || !TermoHqImage16 || !gaussBuff || !irImageBuff)
//    	return -1;
    return 0;
}


int8_t AllocPaletteImageBuffer(void)
{
	pPaletteImage = heap_caps_malloc(((MAX_TEMP - MIN_TEMP) * 10) << 1, MALLOC_CAP_8BIT);	// ????????
	if (!pPaletteImage)
		return -1;
	return 0;
}

//==============================================================================
//
//==============================================================================
void ui_task(void* arg)
{
	//printf("into ui_task 0.\r\n");

	Need2RedrawTitle = 1;
	dispcolor_ClearScreen();
	
	while (1)
    {
		vTaskDelay(100 / portTICK_PERIOD_MS);
    	switch (ScaleMode)
    	{
    	case ByNearest:
    		imageHeight = termHeight * iSteps;
			imageWidth = termWidth * iSteps;
    		break;
    	case LineInterpol:
    		//imageHeight = (termHeight - 1) * iSteps;
			//imageWidth = (termWidth - 1) * iSteps;
			imageHeight = (termHeight - 1) * 8;
			imageWidth = (termWidth - 1) * 8;
    		break;
    	case HQ3X_2X:
    		imageHeight = termHeight * 8;
			imageWidth = termWidth * 8;
    		break;
    	}


    	int16_t imageY = (dispHeight - imageHeight) >> 1;	// The coordinate of the location of the thermogram on the display
		sMlxData *pMlxData = &MlxData[(MlxDataIdx + 1) & 1];
		float *ThermoImage = pMlxData->ThermoImage;

		// ������������ ����� ����� ���� ����������
		if (AutoScaleMode)
		{
			minTempNew = pMlxData->minT;
			maxTempNew = pMlxData->maxT;
		}
	
		if (Need2RedrawTitle)
		{
			Need2RedrawTitle = 0;
			ReDrawTitle();
			printf("ui_task 1.Need2RedrawTitle ... ok\r\n");
		}

		if (Need2RedrawPalette)
		{
			Need2RedrawPalette = 0;
			RedrawPalette();
			printf("ui_task 2.Need2RedrawPalette ... ok\r\n");
		}

		if ((minTempNew != minTemp) || (maxTempNew != maxTemp))
        {
        	// Expand the scale if the range is less MIN_TEMPSCALE_DELTA
        	if (AutoScaleMode)
        	{
        		float Delta = maxTempNew - minTempNew;
        		if (Delta < MIN_TEMPSCALE_DELTA)
        		{
        			minTempNew -= (MIN_TEMPSCALE_DELTA - Delta) / 2;
        			maxTempNew += (MIN_TEMPSCALE_DELTA - Delta) / 2;
        		}
        	}

        	minTemp = minTempNew;
        	maxTemp = maxTempNew;
        	RedrawPalette();
        }

    	// We copy temperatures to an integer array to simplify further calculations
    	for (uint16_t i = 0; i < 768; i++)
		{
        	TermoImage16[i] = ThermoImage[i] * 10;
		}
		

		// Scaling and displaying the thermogram
    	switch (ScaleMode)
    	{
    	case ByNearest:
			printf("ui_task ByNearest mode");
        	DrawImage(TermoImage16, pPaletteImage, PaletteSteps, 0, imageY, 9, 9, minTemp);
    		// Displaying the temperature in the center of the screen
    		DrawCenterTemp(0, imageY, imageWidth, imageHeight, pMlxData->CenterTemp);
			printf(" -> center temperatrue = %.1f\r\n", pMlxData->CenterTemp);
	    	//  MAX / MIN markers on the thermogram
    		if (TempMarkers)
			{
    			DrawMarkers(0, imageY, pMlxData);
			}
    		break;
    	case LineInterpol:
			printf("ui_task LineInterpol mode");
        	idwOldInterpolate(TermoImage16, termWidth, termHeight, 8/*iSteps*/, TermoHqImage16);
       		DrawHQImage(TermoHqImage16, pPaletteImage, PaletteSteps, 16, imageY, imageWidth, imageHeight, minTemp);
    		// Displaying the temperature in the center of the screen
    		DrawCenterTemp(16, imageY, imageWidth, imageHeight, pMlxData->CenterTemp);
			printf(" -> center temperatrue = %.1f\r\n", pMlxData->CenterTemp);
	    	//  MAX / MIN markers on the thermogram
    		if (TempMarkers)
			{
    			DrawMarkers(16, imageY, pMlxData);
			}
    		break;
    	case HQ3X_2X:
			printf("ui_task HQ3X_2X mode");
			idwGauss(TermoImage16, termWidth, termHeight, gaussBuff);
			idwBilinear(gaussBuff, termHeight * 2, termWidth * 2, irImageBuff, termHeight * 8, termWidth * 8, 8 / 2);
       		DrawHQImage(irImageBuff, pPaletteImage, PaletteSteps, 12, imageY, imageWidth, imageHeight, minTemp);
    		// Displaying the temperature in the center of the screen
    		DrawCenterTemp(12, imageY, imageWidth, imageHeight, pMlxData->CenterTemp);
			printf(" -> center temperatrue = %.1f\r\n", pMlxData->CenterTemp);
	    	//  MAX / MIN markers on the thermogram
    		if (TempMarkers)
			{
    			DrawMarkersHQ(12, imageY, pMlxData);
			}
    		break;
    	}

    	// Displaying the minimum and maximum temperature in the frame
		dispcolor_printf_Bg(25, 228, FONTID_6X8M, RGB565(192, 32, 32), BLACK, "MIN=%.1f�C", pMlxData->minT);
		dispcolor_printf_Bg(93, 228, FONTID_6X8M, RGB565(160, 96, 0), BLACK, "MAX=%.1f�C", pMlxData->maxT);
        dispcolor_printf_Bg(160, 228, FONTID_6X8M, RGB565(0, 160, 160), BLACK, "Ta=%.1f�C", pMlxData->Ta);
        dispcolor_printf_Bg(220, 228, FONTID_6X8M, RGB565(96, 160, 0), BLACK, "E=%.2f ", Emissivity);

		// We calculate and display the battery charge and its voltage
		float capacity = pMlxData->VBAT * 125 - 400;
		if (capacity > 100)
			capacity = 100;
		if (capacity < 0)
			capacity = 0;
		DrawBattery(235, 2, capacity);
		dispcolor_printf_Bg(165, 4, FONTID_6X8M, RGB565(160, 96, 0), BLACK, "VBAT=%.2fV ", pMlxData->VBAT);

		// Updating the display from the framebuffer
    	dispcolor_Update();

    	// Handling button clicks
		//ProcessButtonEvents();

    }
}
//==============================================================================
